package cn.wolfcode.shiro;

import cn.wolfcode.domain.Employee;
import cn.wolfcode.mapper.EmployeeMapper;
import cn.wolfcode.mapper.PermissionMapper;
import cn.wolfcode.mapper.RoleMapper;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.management.relation.Role;
import java.security.Permission;
import java.util.ArrayList;
import java.util.List;

@Component
public class CrmRealm extends AuthorizingRealm {
    @Autowired
    private EmployeeMapper mapper;
    @Autowired
    private EmployeeMapper employeeMapper;
    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private RoleMapper roleMapper;

    //将容器中的配置的凭证匹配器注入给当前的 Realm 对象
    //在该 Realm 中使用指定的凭证匹配器来完成密码匹配的操作
    //向spring容器中注入我们自己指定的凭证匹配器(密码匹配功能)
    @Autowired
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
        super.setCredentialsMatcher(credentialsMatcher);
    }

    //1.登录认证:提供认证信息数据
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //1.通过token获取用户名
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        String username = usernamePasswordToken.getUsername();

        //2.查询数据库中是否存在该用户名
        Employee employee = employeeMapper.selectByName(username);
        //3.//若存在,就把用户名和密码封装到AuthenticationInfo中,最后返回给shiro使用
        if ( null != employee) {
            //参数1:用户名:员工对象-供授权时用这里传什么subject就绑定什么.
            //参数2:用户的真正密码.
            // 参数3:加盐(密码加密:和注册时使用的盐相同).
            return new SimpleAuthenticationInfo(employee, employee.getPassword(),
                    ByteSource.Util.bytes(employee.getName()), this.getName());//this.getName():当前Realm数据源名称
        }

        //模拟数据库数据
        //若存在,就把用户名和密码封装到AuthenticationInfo中,最后返回给shiro使用
        //参数1:用户名:员工对象-供授权时用这里传什么subject就绑定什么.
        // 参数2:用户的真正密码.参数3:当前数据源的名字(对我们来说现在没有)
        //return new SimpleAuthenticationInfo(name,password, this.getName());
        //}
        return null;//若用户名不存在,返回null
    }

    //2.授权检查:提供授权信息数据(当前登录用户拥有的)principals封装了页面员工的所有信息
    //(每次shiro需要权限判断(编程/注解式/标签式)都需要调用doGetAuthorizationInfo此方法)
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //检查缓存(若控制台打印了,说明缓存没成功)
        System.out.println("========缓存检测===========");
        //0.若框架的参数没有提供给我们,可通过工具获取
        //Subject subject = SecurityUtils.getSubject();
        //Employee employee = (Employee)subject.getPrincipal();


        //1.获取当前登录用户的对象(参数已提供:框架已把授权相关数据封装到principals对象中,直接获取)
        Employee employee = (Employee) principals.getPrimaryPrincipal();
        //2.info对象中的数据就是当前登录用户的权限和角色信息
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //3.判断是否为超级管理员:若不是查询出角色和权限信息放入info对象中,供shiro管理
        if (!employee.isAdmin()) {//NO管理员
            //4.根据员工id查询员工的角色编码集合
            List<String> roles = roleMapper.selectSnByEmpId(employee.getId());
            info.addRoles(roles);
            //5.根据员工id查询员工的权限表达式集合
            List<String> expressions = permissionMapper.selectExpressionByEmpId(employee.getId());
            info.addStringPermissions(expressions);
        } else {//YES管理员
            info.addStringPermission("*:*");//通配符:代表拥有所有权限
            info.addRole("ADMIN");//管理员角色:在这里添加了方便直接使用subject判断是否是管理员
        }
        return info;
    }
}